package utils;

import java.util.ArrayList;
import java.util.Arrays;
//import java.util.logging.Level;
//import java.util.logging.Logger;
import org.biojava.bio.structure.AminoAcid;
//import org.biojava.bio.structure.Calc;
import org.biojava.bio.structure.StructureException;
import rotlib.Rotamer;
import rotlib.RotamerLibrary;
//import org.biojava.bio.structure.AminoAcid;
public class RotamerLibraryUtil {

    public RotamerLibraryUtil() { }

   public static Rotamer getBestRotamerMod(RotamerLibrary rl, AminoAcid aa){
        int iAminoAcidIndex = Arrays.binarySearch(ListUtil.THREE_LETTER_CODE, aa.getPDBName());
        String[] sideChainAtomList = ListUtil.SIDE_CHAIN_ATOM_LIST[iAminoAcidIndex];
        int iBestRotamerAccuracy = 0, iTmpRotamerAccuracy;
        Rotamer bestRotamer = null, r;
        double rotTorAngle,iangle;
        ArrayList<Double> aaTorAngle = new ArrayList<Double>(); 
        if (rl == null || rl.getRotamer() == null)
            return null;

        //calcula los angulos de torsionCadenaLateral para el aminoacido aa
        for (int j = 3; j < sideChainAtomList.length; j++){
             try {
                    if (aa.hasAtom(sideChainAtomList[j - 3]) && aa.hasAtom(sideChainAtomList[j - 2]) && aa.hasAtom(sideChainAtomList[j - 1]) && aa.hasAtom(sideChainAtomList[j])) {
                         iangle = StructureUtil.getTorsionAngle(aa.getAtom(sideChainAtomList[j - 3]), 
                                                                aa.getAtom(sideChainAtomList[j - 2]), 
                                                                aa.getAtom(sideChainAtomList[j - 1]), 
                                                                aa.getAtom(sideChainAtomList[j]));
                         aaTorAngle.add(iangle);
                    }
                } 
                catch (StructureException ex) { ex.printStackTrace();}
         }
        for (int i = 0; i < rl.getRotamer().size(); i++) {
            r = rl.getRotamer().get(i);
            iTmpRotamerAccuracy = 0;

            for (int k = 0; k < aaTorAngle.size(); k++) {
                         rotTorAngle = r.getChiValue(k + 1);
                         if (StructureUtil.isAngleOK(aaTorAngle.get(k), rotTorAngle, 40)) {
                            iTmpRotamerAccuracy++;
                         }
                         else {
                            break;
                         }
                    
           }

            if (bestRotamer == null || iBestRotamerAccuracy < iTmpRotamerAccuracy || (iBestRotamerAccuracy == iTmpRotamerAccuracy && bestRotamer.getProbabilityValue() < r.getProbabilityValue())) {
                bestRotamer = r;
                iBestRotamerAccuracy = iTmpRotamerAccuracy;
            }
        }
        
         return bestRotamer;
   } 
    
    /**
     * 
     * @param rl colección  de rotámeros para el residuo (aa)
     * @param aa residuo
     * @return rotámero que tenga el mayor número de chi correctas, es decir :
     *         * la diferencia entre chi y el angulo de torsión correspondiente,
     *           sea menor o igual a 40.
     * 
     * ejemplo:
     *     Rgln ={r1,r2,...r|Rgln|} // colección romátero Glutamina
     *     r1 ={x1,x2,x3}  
     *     AnguloTorsion ={Angulo1,Angulo2, Angulo3}   
     *     Angulo1 =N,CA,CB,CG  //atomos que corresponden al ángulo de torsión
     *     Angulo2 =CA,CB,CG,CD
     *     Angulo3 =CB,CG,CD,OE1
     * 
     *      anguloCorrectosri = sumatoria j hasta ri anguloCorrecto(anguloj,chij)
     *       
     *      anguloCorrecto = 1  si anguloCorrecto <= 40 grados
     *      angulo correcto = 0 anguli correcto > 40 grados
     * 
     *      Max = r pertenece Rgln angulosCorrectosr
     *         
     *          
     */
    public static Rotamer getBestRotamer(RotamerLibrary rl, AminoAcid aa) {
        int iAminoAcidIndex = Arrays.binarySearch(ListUtil.THREE_LETTER_CODE, aa.getPDBName());
        String[] sideChainAtomList = ListUtil.SIDE_CHAIN_ATOM_LIST[iAminoAcidIndex];
        int iBestRotamerAccuracy = 0, iTmpRotamerAccuracy;
        Rotamer bestRotamer = null, r;
        double aaTorAngle;
        double rotTorAngle;

        if (rl == null || rl.getRotamer() == null)
            return null;
       // System.out.println("posibilidades:" + rl.getRotamer().size());
        for (int i = 0; i < rl.getRotamer().size(); i++) {
            r = rl.getRotamer().get(i);
            iTmpRotamerAccuracy = 0;

            for (int j = 3; j < sideChainAtomList.length; j++) {
                try {
                    if (aa.hasAtom(sideChainAtomList[j - 3]) && aa.hasAtom(sideChainAtomList[j - 2]) && aa.hasAtom(sideChainAtomList[j - 1]) && aa.hasAtom(sideChainAtomList[j])) {
                        //System.out.println("j-3: "+ aa.getAtom(sideChainAtomList[j - 3]) +" j-2: " + aa.getAtom(sideChainAtomList[j - 2])+" j-1: "+aa.getAtom(sideChainAtomList[j - 1])+" j:  "+ aa.getAtom(sideChainAtomList[j]) );
                        //observacion : se vuelve a calcular el mismo angulo de torsion,
                        //cuando es el mismo si no se cumple la condiciones is ANgle ok
                        aaTorAngle = StructureUtil.getTorsionAngle(aa.getAtom(sideChainAtomList[j - 3]), aa.getAtom(sideChainAtomList[j - 2]), aa.getAtom(sideChainAtomList[j - 1]), aa.getAtom(sideChainAtomList[j]));
                         rotTorAngle = r.getChiValue(j - 2);
                         if (StructureUtil.isAngleOK(aaTorAngle, rotTorAngle, 40)) {
                            iTmpRotamerAccuracy++;
                         }
                         else {
                            break;
                         }
                    }
                } 
                catch (StructureException ex) {
                    ex.printStackTrace();
                }
            }

            if (bestRotamer == null || iBestRotamerAccuracy < iTmpRotamerAccuracy || (iBestRotamerAccuracy == iTmpRotamerAccuracy && bestRotamer.getProbabilityValue() < r.getProbabilityValue())) {
                bestRotamer = r;
                iBestRotamerAccuracy = iTmpRotamerAccuracy;
            }
        }
        return bestRotamer;
    }

    public static Rotamer getBestRotamerRMSD(RotamerLibrary rl, AminoAcid aa) {
        Rotamer r;
        AminoAcid aa2;
        double bestRMSD, actualRMSD;
        int bestRotamerNumber;

        if (rl == null || rl.getRotamer() == null)
            return null;
        
        r = rl.getRotamer().get(0);
        aa2 = StructureUtil.createAminoacid(aa, r);
        bestRMSD = Double.parseDouble(StructureUtil.getRMSD(aa, aa2));
        bestRotamerNumber = 0;
        for (int i = 1; i < rl.getRotamer().size(); i++) {
            r = rl.getRotamer().get(i);
            aa2 = StructureUtil.createAminoacid(aa, r);
            actualRMSD = Double.parseDouble(StructureUtil.getRMSD(aa, aa2));
            if(actualRMSD < bestRMSD) {
               bestRMSD = actualRMSD;
               bestRotamerNumber = i;
            }
        }
        return rl.getRotamer().get(bestRotamerNumber);
    }

    public static int getNumberBestRotamer(RotamerLibrary rl, AminoAcid aa) {
        int bestRotamerIndex  = 0;
        int iAminoAcidIndex = Arrays.binarySearch(ListUtil.THREE_LETTER_CODE, aa.getPDBName());
        String[] sideChainAtomList = ListUtil.SIDE_CHAIN_ATOM_LIST[iAminoAcidIndex];
        int iBestRotamerAccuracy = 0;
        Rotamer bestRotamer = null;

        if (rl == null || rl.getRotamer() == null)
            return -1;

        for (int i = 0; i < rl.getRotamer().size(); i++) {
            Rotamer r = rl.getRotamer().get(i);
            int iTmpRotamerAccuracy = 0;

            for (int j = 3; j < sideChainAtomList.length; j++)  {
                try {
                    if (aa.hasAtom(sideChainAtomList[j - 3]) && aa.hasAtom(sideChainAtomList[j - 2]) && aa.hasAtom(sideChainAtomList[j - 1]) && aa.hasAtom(sideChainAtomList[j]))  {
                          double aaTorAngle = StructureUtil.getTorsionAngle(aa.getAtom(sideChainAtomList[j - 3]), aa.getAtom(sideChainAtomList[j - 2]), aa.getAtom(sideChainAtomList[j - 1]), aa.getAtom(sideChainAtomList[j]));
                          double rotTorAngle = r.getChiValue(j - 2);
                          if (StructureUtil.isAngleOK(aaTorAngle, rotTorAngle, 40)) {
                               iTmpRotamerAccuracy++;
                          } else {
                               break;
                          }
                    }
                } 
                catch (StructureException ex) {
                    ex.printStackTrace();
                }
            }

            if (bestRotamer == null || iBestRotamerAccuracy < iTmpRotamerAccuracy || (iBestRotamerAccuracy == iTmpRotamerAccuracy && bestRotamer.getProbabilityValue() < r.getProbabilityValue())) {
                bestRotamerIndex = i;
                bestRotamer = r;
                iBestRotamerAccuracy = iTmpRotamerAccuracy;
            }
        }
        return bestRotamerIndex;
    }

    public static int getNumberBestRotamerRMSD(RotamerLibrary rl, AminoAcid aa) {
        Rotamer r;
        AminoAcid aa2;
        double bestRMSD, actualRMSD;
        int bestRotamerNumber;

        if (rl == null || rl.getRotamer() == null)
            return -1;

        r = rl.getRotamer().get(0);
        aa2 = StructureUtil.createAminoacid(aa, r);
        bestRMSD = Double.parseDouble(StructureUtil.getRMSD(aa, aa2));
        bestRotamerNumber = 0;
        for (int i = 1; i < rl.getRotamer().size(); i++) {
            r = rl.getRotamer().get(i);
            aa2 = StructureUtil.createAminoacid(aa, r);
            actualRMSD = Double.parseDouble(StructureUtil.getRMSD(aa, aa2));
            if(actualRMSD < bestRMSD) {
               bestRMSD = actualRMSD;
               bestRotamerNumber = i;
            }
        }
        return bestRotamerNumber;
    } 
}
